﻿using Bouyei.GeoCore.Geometries;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Bouyei.GeoCore.GeoParsers.Vct
{
    public class VctInfo
    {
        public VctHeader vctHeader { get; set; }

        public VctFeatureCode vctFeatureCode { get; set; }

        public VctTableStructure vctTableStructure { get; set; }

        public VctGeoGraphic vctGeoGraphic { get; set; }

        public VctAnnotation vctAnnontation { get; set; }

        public VctTopologyItem vctTopology { get; set; }

        public VctAttributeItem vctAttribute { get; set; }

        public VctStyleItem vctStyle { get; set; }
    }

    public class VctItemBase
    {
        protected Type subClassType { get; private set; }
        protected string StartFlag { get; private set; } = string.Empty;
        protected string EndFlag { get; private set; } = string.Empty;
        protected VctHeader vctHeader { get; private set; }

        protected PropertyInfo[] properties { get; private set; }
        protected static char[] seperatorCache { get; private set; }

        public VctItemBase() { }

        public VctItemBase(Type subClassType)
        {
            if (subClassType == null)
                this.subClassType = this.GetType();
            else
                this.subClassType = subClassType;

            if (subClassType != null)
                properties = subClassType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        }

        public VctItemBase(Type subClassType, string startFlag, string endFlag)
            : this(subClassType)
        {
            this.StartFlag = startFlag;
            this.EndFlag = endFlag;
        }

        public VctItemBase(Type subClassType, VctHeader vctHeader, string startFlag, string endFlag)
            : this(subClassType, startFlag, endFlag)
        {
            this.vctHeader = vctHeader;
            if (this.vctHeader != null && this.vctHeader.Separator != null)
                seperatorCache = this.vctHeader.Separator.ToCharArray();
        }

        public virtual bool FromReader(StreamReader reader)
        {
            return FromReader(reader, this);
        }

        public virtual bool FromReader(StreamReader reader, object instance)
        {
            bool isMoved = MoveStartFlagPosition(reader);
            if (isMoved == false) return false;

            bool isTrue = false;
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                if (line == EndFlag) break;

                var array = line.Trim().Split(':');
                if (array.Length == 1) continue;

                var setPro = GetProPerty(array[0]);
                if (setPro == null) continue;

                object v = Convert.ChangeType(array[1], setPro.PropertyType);
                setPro.SetValue(instance, v, null);
                isTrue = true;
            }
            return isTrue;
        }

        public virtual bool FromStringLine(string line, object instance)
        {
            bool isTrue = false;
            var array = line.Split(seperatorCache);
            for (int i = 0; i < properties.Length; ++i)
            {
                if (i >= array.Length) break;
                var setPro = properties[i];

                object v = Convert.ChangeType(array[i], setPro.PropertyType);
                setPro.SetValue(instance, v, null);
                isTrue = true;
            }
            return isTrue;
        }

        protected bool MoveStartFlagPosition(StreamReader reader)
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                if (line == StartFlag) return true;
            }
            return false;
        }

        private PropertyInfo GetProPerty(string name)
        {
            foreach (var pro in properties)
            {
                if (name == pro.Name) return pro;
            }
            return null;
        }
    }

    public class VctHeader : VctItemBase
    {
        public string DataMark { get; set; }

        public string Version { get; set; }

        public string CoordinateSystemType { get; set; }
        public int Dim { get; set; }
        public string XAxisDirection { get; set; }

        public string YAxisDirection { get; set; }

        public string XYUnit { get; set; }
        public string ZUnit { get; set; }
        /// <summary>
        /// format CGCS2000,6378137.0,298.257222101
        /// </summary>
        public string Spheroid { get; set; }

        public string PrimeMeridian { get; set; }
        /// <summary>
        /// 高斯-克吕格投影
        /// </summary>
        public string Projection { get; set; }
        /// <summary>
        /// 108.000000,1,36500000.000000,0.000000,3,36
        /// </summary>
        public string Parameters { get; set; }
        /// <summary>
        /// 国家高程基准
        /// </summary>
        public string VerticalDatum { get; set; }

        public string TemporalReferenceSystem { get; set; }
        /// <summary>
        /// 36487405.230300,2963266.548800
        /// </summary>
        public string ExtentMin { get; set; }
        public string ExtentMax { get; set; }

        public int MapScale { get; set; }
        /// <summary>
        /// 0,0
        /// </summary>
        public string Offset { get; set; }
        /// <summary>
        /// 20200119
        /// </summary>
        public string Date { get; set; }
        /// <summary>
        /// ,
        /// </summary>
        public string Separator { get; set; }

        public VctHeader()
            : base(typeof(VctHeader), "HeadBegin", "HeadEnd")
        {

        }
    }

    public class VctFeatureCode : VctItemBase
    {
        public VctFCodeItem[] FCodeItem { get; set; }

        public VctFeatureCode(VctHeader vctHeader)
            : base(typeof(VctFCodeItem), vctHeader, "FeatureCodeBegin", "FeatureCodeEnd")
        { }

        public override bool FromReader(StreamReader reader)
        {
            bool isMoved = MoveStartFlagPosition(reader);
            if (isMoved == false) return false;

            var list = new List<VctFCodeItem>(64);
            string line;

            while ((line = reader.ReadLine()) != null)
            {
                if (line == EndFlag) break;

                VctFCodeItem fCodeItem = new VctFCodeItem();
                bool _isTrue = FromStringLine(line, fCodeItem);
                if (_isTrue) list.Add(fCodeItem);
            }

            if (list.Count > 0) FCodeItem = list.ToArray();

            return FCodeItem.Length > 0;
        }
    }

    public class VctFCodeItem
    {
        /// <summary>
        /// 要素代码
        /// </summary>
        public string FCode { get; set; }
        /// <summary>
        /// table昵称
        /// </summary>
        public string FName { get; set; }

        /// <summary>
        /// Polygon,Line,Point,Annotation
        /// </summary>
        public string FType { get; set; }
        /// <summary>
        /// 表名
        /// </summary>
        public string FTableName { get; set; }
    }

    public class VctTableStructure : VctItemBase
    {
        public List<VctTableStructureItem> vctTables { get; set; }

        public VctTableStructure(VctHeader vctHeader)
            : base(null, vctHeader, "TableStructureBegin", "TableStructureEnd")
        { }

        public override bool FromReader(StreamReader reader)
        {
            bool isMoved = MoveStartFlagPosition(reader);
            if (isMoved == false) return false;

            var list = new List<VctTableStructureItem>(64);
            string line;
            VctTableStructureItem vctTable = null;

            while ((line = reader.ReadLine()) != null)
            {
                if (line == EndFlag) break;
                if (line == string.Empty)
                {
                    if (vctTable != null) list.Add(vctTable);

                    vctTable = null;
                    continue;
                }

                if (vctTable == null)
                {
                    var array = line.Split(seperatorCache);
                    vctTable = new VctTableStructureItem()
                    {
                        TableName = array[0],
                        ColumnCount = Convert.ToInt32(array[1])
                    };
                    vctTable.Columns = new List<VctTTableItem>(vctTable.ColumnCount);
                    continue;
                }

                var columns = line.Split(seperatorCache);
                if (columns.Length <= 1) continue;

                int size = 0;
                if (columns.Length >= 3)
                {
                    int.TryParse(columns[2], out size);
                }
                vctTable.Columns.Add(new VctTTableItem()
                {
                    ColumnName = columns[0],
                    ColumnType = columns[1],
                    ColumnSize = size
                });
            }

            if (vctTable != null) list.Add(vctTable);
            return list.Count > 0;
        }
    }

    public class VctTableStructureItem
    {
        public string TableName { get; set; }

        public int ColumnCount { get; set; }

        public List<VctTTableItem> Columns { get; set; }
    }

    public class VctTTableItem
    {
        public string ColumnName { get; set; }

        public string ColumnType { get; set; }

        public int ColumnSize { get; set; }

        public int precision { get; set; }
    }

    public class VctPoint : VctItemBase
    {
        public VctPointItem[] Points { get; set; }

        public VctPoint(VctHeader vctHeader)
            : base(typeof(VctPointItem), vctHeader, "PointBegin", "PointEnd")
        {

        }
        public override bool FromReader(StreamReader reader)
        {
            bool isTrue = MoveStartFlagPosition(reader);
            if (isTrue == false) return false;

            string line = reader.ReadLine();
            if (line == EndFlag) return false;

            List<VctPointItem> points = new List<VctPointItem>(32);

            while ((line = reader.ReadLine()) != null)
            {
                if (line == EndFlag) break;
                if (line == "0" || line == string.Empty) continue;

                VctPointItem pItem = new VctPointItem();

                pItem.BSM = line;

                line = reader.ReadLine();
                pItem.YSDM = line;

                line = reader.ReadLine();
                pItem.RepresentationCoding = line;

                line = reader.ReadLine();
                pItem.FeatureType = int.Parse(line);

                line = reader.ReadLine();
                pItem.RepresentationCoding = line;

                line = reader.ReadLine();
                pItem.FeatureType = int.Parse(line);

                line = reader.ReadLine();
                pItem.Count = int.Parse(line);
                pItem.Points = new List<Coordinate>(pItem.Count);

                while ((line = reader.ReadLine()) != null)
                {
                    if (line == "0" || line == string.Empty) break;

                    var lonlat = line.Split(seperatorCache);
                    pItem.Points.Add(new Coordinate()
                    {
                        X = double.Parse(lonlat[0]),
                        Y = double.Parse(lonlat[1])
                    });
                }
                points.Add(pItem);
            }

            Points = points.ToArray();

            return Points.Length > 0;
        }
    }

    public class VctLine : VctItemBase
    {
        public VctLineStringItem[] LineString { get; set; }

        public VctLine(VctHeader vctHeader)
           : base(typeof(VctPointItem), vctHeader, "LineBegin", "LineEnd")
        {

        }

        public override bool FromReader(StreamReader reader)
        {
            bool isTrue = MoveStartFlagPosition(reader);
            if (isTrue == false) return false;

            string line = reader.ReadLine();
            if (line == EndFlag) return false;

            List<VctLineStringItem> lineStrings = new List<VctLineStringItem>(32);

            while ((line = reader.ReadLine()) != null)
            {
                if (line == EndFlag) break;
                if (line == "0" || line == string.Empty) continue;

                VctLineStringItem pItem = new VctLineStringItem();

                pItem.BSM = line;

                line = reader.ReadLine();
                pItem.YSDM = line;

                line = reader.ReadLine();
                pItem.RepresentationCoding = line;

                line = reader.ReadLine();
                pItem.FeatureType = int.Parse(line);

                line = reader.ReadLine();
                pItem.FeatureType = int.Parse(line);

                line = reader.ReadLine();
                pItem.SegmentType = int.Parse(line);

                line = reader.ReadLine();
                pItem.BSM = line;
                pItem.Points = new List<Coordinate>(pItem.Count);

                while ((line = reader.ReadLine()) != null)
                {
                    if (line == "0" || line == string.Empty) break;

                    var lonlat = line.Split(seperatorCache);
                    pItem.Points.Add(new Coordinate()
                    {
                        X = double.Parse(lonlat[0]),
                        Y = double.Parse(lonlat[1])
                    });
                }
                lineStrings.Add(pItem);
            }

            LineString = lineStrings.ToArray();

            return LineString.Length > 0;
        }
    }

    public class VctGeoGraphic : VctItemBase
    {
        public VctPointItem[] Points { get; set; }

        public VctLineStringItem[] LineStrings { get; set; }

        public VctPolygonItem[] Polygon { get; set; }

        private GeoType geometryType;

        public VctGeoGraphic(VctHeader vctHeader, GeoType geometryType)
            : base(GetType(geometryType),
                vctHeader, GetFlag(geometryType) + "Begin", GetFlag(geometryType) + "End")
        {
            this.geometryType = geometryType;
        }

        private static Type GetType(GeoType geometryType)
        {
            return geometryType == GeoType.POINT ? typeof(VctPointItem)
                  : geometryType == GeoType.LINESTRING ? typeof(VctLineStringItem)
                  : geometryType == GeoType.POLYGON ? typeof(VctPolygonItem) : null;
        }

        private static string GetFlag(GeoType geometryType)
        {
            return geometryType == GeoType.POINT ? "Point"
                  : geometryType == GeoType.LINESTRING ? "Line"
                  : geometryType == GeoType.POLYGON ? "Polygon" : null;
        }

    }

    public class VctPointItem : VctGraphBase
    {
        /// <summary>
        /// 坐标点个数
        /// </summary>
        public int Count { get; set; }
        /// <summary>
        /// 坐标点集合
        /// </summary>
        public List<Coordinate> Points { get; set; }
    }

    public class VctLineStringItem : VctGraphBase
    {
        /// <summary>
        /// 线段条数
        /// </summary>
        public int SegmentCount { get; set; }
        /// <summary>
        /// 线段类型
        /// </summary>
        public int SegmentType { get; set; }
        /// <summary>
        /// 坐标点个数
        /// </summary>
        public int Count { get; set; }

        public List<Coordinate> Points { get; set; }
    }

    public class VctPolygonItem : VctItemBase
    {
        /// <summary>
        /// 标识坐标点
        /// </summary>
        public Coordinate IdentifyPoint { get; set; }

        /// <summary>
        /// 间接坐标面构成类型，21表示引用线对象构成的面，22表示引用面对象构成的面
        /// </summary>
        public int IndirectType { get; set; }

        /// <summary>
        /// 对象个数
        /// </summary>
        public int ObjectCount { get; set; }

        /// <summary>
        /// 对象标识号集合
        /// </summary>
        public int[] ObjectIdCollection { get; set; }

        public VctPolygonItem(VctHeader vctHeader)
            : base(typeof(VctPolygonItem), vctHeader, "", "")
        { }
    }

    public class VctGraphBase
    {
        /// <summary>
        /// 标识码
        /// </summary>
        public string BSM { get; set; }
        /// <summary>
        /// 要素代码
        /// </summary>
        public string YSDM { get; set; }
        /// <summary>
        /// 图形表现编码
        /// </summary>
        public string RepresentationCoding { get; set; }
        /// <summary>
        /// 点的特征类型：1|2|3,1表示独立点，2表示结点，3表示有向点
        /// 线的特征类型：1|2|3|4|5|6|100，1表示折线、2表示圆弧、3表示园、4表示椭圆、5表示光滑曲线、6表示B样条曲线、100表示间接坐标
        /// 面的特征类型：1表示由直接坐标表示的面对象，100表示由间接坐标表示的面对象
        /// </summary>
        public int FeatureType { get; set; }
    }

    public class VctAnnotation
    {
        /// <summary>
        /// 颜色值
        /// </summary>
        public int IColor { get; set; }
        /// <summary>
        /// 注记要素项
        /// </summary>
        public VctAnnotationBase Items { get; set; }
    }

    public class VctAnnotationBase
    {
        /// <summary>
        /// 标识码
        /// </summary>
        public int ICode { get; set; }
        /// <summary>
        /// 要素类
        /// </summary>
        public string FType { get; set; }
        /// <summary>
        /// 图形表现码
        /// </summary>
        public string GRCode { get; set; }
        /// <summary>
        /// 要素编号
        /// </summary>
        public string FNo { get; set; }
        /// <summary>
        /// 标识内容
        /// </summary>
        public string RContent { get; set; }
        /// <summary>
        /// 注记数
        /// </summary>
        public int PointCount { get; set; }
        /// <summary>
        /// 点数
        /// </summary>
        public List<VctAnnotaionPoint> Points { get; set; }
    }

    public class VctAnnotaionPoint
    {
        public Coordinate point { get; set; }

        public double Angle { get; set; }
    }

    public class VctTopologyItem
    {
        public VctNodeTopology TopoNodes { get; set; }

        public VctEdgeTopology TopoEdage { get; set; }
    }

    public class VctNodeTopology
    {
        public List<VctNodeTopologyBase> NodePologys { get; set; }
    }

    public class VctNodeTopologyBase
    {
        public int ICode { get; set; }

        public int PointCount { get; set; }

        public int[] PointNo { get; set; }

        public int PColor { get; set; }
    }

    public class VctEdgeTopology
    {
        public List<VctEdgeTopologyBase> Egdes { get; set; }
    }

    public class VctEdgeTopologyBase
    {
        public int ICode { get; set; }

        public int StartNo { get; set; }

        public int EndNo { get; set; }

        public int LeftNo { get; set; }

        public int RightNo { get; set; }
    }

    public class VctAttributeItem
    {
        public List<string> TableItems { get; set; }

        public List<string> VarcharItems { get; set; }
    }

    public class VctStyleItem
    {
        public List<VctRepresentationItem> RepresentationItems { get; set; }
    }

    public class VctRepresentationItem
    {
        public string Style { get; set; }

        public string Name { get; set; }

        public int Number { get; set; }

        public int Color { get; set; }

        public int Symbolid { get; set; }

        public float PointSize { get; set; }

        public int FColor { get; set; }
    }
}
